package docbookarchivemanager.converter;

import docbookarchivemanager.StartupManager;
import docbookarchivemanager.utils.FileToCopy;
import docbookarchivemanager.utils.DocbookFile;
import docbookarchivemanager.utils.FileManager;
import docbookarchivemanager.utils.StringHelper;
import docbookarchivemanager.utils.Stylesheet;
import docbookarchivemanager.utils.ZipManager;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

/**
 *
 * @author Jakub Hrachovec
 */
public class Converter {

    public static final String MANIFEST_NAME = "manifest.xml";
    public static final String DOCBOOK_ARCHIVE_EXTENSION = ".dar";
    private static Mode mode = Mode.DEFAULT;
    private static File userEnteredDbFile;

    private static ArrayList<FileToCopy> listOfFilesToCopy = new ArrayList<>();
    private static ArrayList<DocbookFile> listOfIncludedFilesToGoThrough = new ArrayList<>();
    private static ArrayList<Stylesheet> stylesheets = new ArrayList<>();
    private static HashMap<String, String> filesIdentsAndLocations = new HashMap<>();

    public static enum Mode {

        DEFAULT(),
        USER_MODE();

        private Mode() {
        }

    }

    /**
     * Starts conversion of Docbook XML file user entered.
     *
     * @param mainDbFile
     * @param outputFile
     * @throws IOException
     * @throws NullPointerException
     */
    public static void convert(File mainDbFile, File outputFile) throws IOException, NullPointerException {
        File output;
        String fileName;
        if (outputFile != null) {
            output = outputFile.getParentFile();
            fileName = outputFile.getName();
        } else {
            output = StartupManager.getGlobalParentFolder();
            fileName = FileManager.getFileName(mainDbFile) + DOCBOOK_ARCHIVE_EXTENSION;
        }

        //Adds manifest to the file list beforehand.
        addFileIdentAndLocation("MANIFEST", new File(StartupManager.getGlobalOutputFolder() + File.separator + MANIFEST_NAME).getCanonicalPath());

        //Start processing of main Docbook XML.
        processFile(mainDbFile, "", null, DocbookFile.Type.XML, false);

        for (int i = 0; i < listOfIncludedFilesToGoThrough.size(); i++) {
            ReplacementManager.processFile(listOfIncludedFilesToGoThrough.get(i));
        }

        System.out.println("");

        for (FileToCopy filet : listOfFilesToCopy) {
            FileManager.copyFile(filet.getFile(), filet.getAbsolutePath());
        }

        System.out.println("");
//        for (Map.Entry<String, String> cursor : getFilesIdentsAndLocations().entrySet()) {
//            System.out.println(cursor.getKey() + "   " + new File(cursor.getValue()).getCanonicalPath());
//        }

        ManifestCreator.createManifest();

        ZipManager.zipIt(StartupManager.getGlobalOutputFolder(), output, fileName);
    }

    /**
     * Processes given file (e.g from an attribute of an element), and returns
     * new node value that points to the file relative to the file that it
     * points from.
     *
     * @param curNodeFile File from an attribute.
     * @param newFileFolder Path to the folder this file is outputted to
     * relative to the temp folder.
     * @param calledFrom File that references this file.
     * @param dbFileType type of the file(xsl, xml) or null.
     * @param isIncluded Whether the file is supposed to be included. (ie. it
     * isn't the main xml).
     * @param stylesheetType A stylesheetType to add a manifest stylesheet
     * entry.
     * @param isFo
     * @return new node value
     * @throws IOException
     */
    public static String processFile(File curNodeFile, String newFileFolder, File calledFrom, DocbookFile.Type dbFileType, boolean isIncluded, Stylesheet.Type stylesheetType, Stylesheet.IsFormatObject isFo) throws IOException {

        String curNodeFileMd5 = FileManager.generateChecksum(curNodeFile);
        String canPath;
        if (containsFileIdent(curNodeFileMd5)) {
            canPath = new File(getFileIdentAndLocation(curNodeFileMd5)).getCanonicalPath();

        } else {
            String fullOutputPath = StartupManager.getGlobalOutputFolder().getCanonicalPath() + File.separator + newFileFolder;
            File newFileToCopy = new File(fullOutputPath + File.separator + FileManager.getUniqueFileName(curNodeFile, fullOutputPath));
            canPath = newFileToCopy.getCanonicalPath();

            if (dbFileType == null) {
                addFileToCopy(new FileToCopy(curNodeFile, curNodeFileMd5, canPath));
            } else {
                DocbookFile dbFile = new DocbookFile(curNodeFile, isIncluded, dbFileType, curNodeFileMd5, canPath);
                addIncludedFileToGoThrough(dbFile);

                if (stylesheetType != null && DocbookFile.Type.XSL.equals(dbFileType)) {
                    addStylesheet(new Stylesheet(StringHelper.convertToRelativePath(StartupManager.getGlobalOutputFolder().getCanonicalPath(), newFileToCopy.getCanonicalPath()), isFo, curNodeFile.getName(), stylesheetType));
                }
            }
            addFileIdentAndLocation(curNodeFileMd5, canPath);

        }

        if (calledFrom == null) {
            return null;
        } else {
            return StringHelper.convertToRelativePath(calledFrom.getParentFile().getCanonicalPath(), canPath);
        }

    }

    /**
     * Processes given file (e.g from an attribute of an element), and returns
     * new node value that points to the file relative to the file that it
     * points from.
     *
     * @param curNodeFile File from an attribute.
     * @param newFileFolder Path to the folder this file is outputted to
     * relative to the temp folder.
     * @param calledFrom File that references this file.
     * @param dbFileType type of the file(xsl, xml) or null.
     * @param isIncluded If the files is supposed to be included. (ie. it isn't
     * the main xml).
     * @return new node value
     * @throws IOException
     */
    public static String processFile(File curNodeFile, String newFileFolder, File calledFrom, DocbookFile.Type dbFileType, boolean isIncluded) throws IOException {
        return processFile(curNodeFile, newFileFolder, calledFrom, dbFileType, isIncluded, null, null);
    }

    public static String processFile(File curNodeFile, String newFileFolder, File calledFrom, DocbookFile.Type dbFileType) throws IOException {
        return processFile(curNodeFile, newFileFolder, calledFrom, dbFileType, true, null, null);
    }

    /**
     * @return the mode
     */
    public static Mode getMode() {
        return mode;
    }

    /**
     * @param aMode the mode to set
     */
    public static void setMode(Mode aMode) {
        mode = aMode;
    }

    /**
     * Adds said Docbook XML or XSL file to queue providing it would be a unique
     * one.
     *
     * @param dbFile
     */
    public static void addIncludedFileToGoThrough(DocbookFile dbFile) {
        listOfIncludedFilesToGoThrough.add(dbFile);
    }

    /**
     * @return list of included Docbook XML or XSL files.
     */
    public static ArrayList<DocbookFile> getListOfIncludedFilesToGoThrough() {
        return listOfIncludedFilesToGoThrough;
    }

    /**
     * Adds a file to be copied to the temporary folder that will be zippped
     * providing it would be a unique one.
     *
     * @param file
     */
    public static void addFileToCopy(FileToCopy file) {
        listOfFilesToCopy.add(file);
    }

    /**
     *
     * @return stylesheets from PIs and command line -s.
     */
    public static ArrayList<Stylesheet> getStylesheets() {
        return stylesheets;
    }

    /**
     * @return the filesIdentsAndLocations
     */
    public static HashMap<String, String> getFilesIdentsAndLocations() {
        return filesIdentsAndLocations;
    }

    /**
     * @param s key to get value for
     * @return value for given key
     */
    public static String getFileIdentAndLocation(String s) {
        return filesIdentsAndLocations.get(s);
    }

    /**
     * @param s key to check
     * @return whether filesIdentsAndLocations contains given key
     */
    public static boolean containsFileIdent(String s) {
        return filesIdentsAndLocations.containsKey(s);
    }

    /**
     * @param s path to check
     * @return whether filesIdentsAndLocations contains given path
     */
    public static boolean containsFileLocation(String s) {
        return filesIdentsAndLocations.containsValue(s);
    }

    /**
     * Adds an entry for a file that was processed.
     *
     * @param ident file identifier.
     * @param canPath Location where the file was processed to.
     */
    public static void addFileIdentAndLocation(String ident, String canPath) {
        filesIdentsAndLocations.put(ident, canPath);
    }

    /**
     * Adds a stylesheet to be listed in manifest.xml.
     *
     * @param stylesheet
     */
    public static void addStylesheet(Stylesheet stylesheet) {
        stylesheets.add(stylesheet);
    }

    /**
     * @return the userEnteredDbFile
     */
    public static File getUserEnteredDbFile() {
        return userEnteredDbFile;
    }

    /**
     * @param aUserEnteredDbFile the userEnteredDbFile to set
     */
    public static void setUserEnteredDbFile(File aUserEnteredDbFile) {
        userEnteredDbFile = aUserEnteredDbFile;
    }

}
